package com.cskayan.javase.object._3equals;

import com.sun.media.jfxmedia.events.NewFrameEvent;

/**
 * equals方法
 * 1.方法的声明，比较简单：
 *      public boolean equals(Object obj)
 *      方法会返回一个布尔值,而且需要传入一个Object对象,
 *          这表示该方法可以传入一切对象
 *
 * 2.方法的作用
 *      equals有相同，相等的意思。
 *      所以该方法的作用是，用于指示其他某个对象是否与此对象"相等"。
 *      此对象即调用该equals方法的对象，其它对象即方法实参传入的对象。
 *      这个方法判断两个对象是否相等
 *      相等返回true,不相等返回false
 *
 * 3.两个对象相等,那么相等的规则是什么呢?
 *      equals方法的实现就是相等的规则
 *
 * 4.Object类当中的默认实现,两个对象相等的规则:
 *       return (this == obj);
 * 用"=="判断两个对象相等,只有当两个引用完全指向同一个对象时,才返回true
 * 这是一个非常苛刻的条件,说白了,只有对象自己才和自己相等.
 *      一般情况下,我们都不需要这么苛刻的比较条件,需要在子类中重写这个方法.
 *
 *
 * 5.我们认为对象相等的规则是什么样的呢?
 *      a.如果两个对象不是同种类型对象,不具有可比性,直接认为不相等
 *      b.如果两个对象是同种类型对象,它们的行为往往是一致的,这时如果它们的属性再一致,就可以认为对象是相等的了.
 *
 * 6.equals方法的重写
 * 重写equals方法的常规协定
 *      1.自反性：对于任何非空引用值 x，x.equals(x)都应返回 true
 *          自己和自己比较,肯定返回true
 *      2.排他性：当比对的不是同种类型的对象或者是一个null时，默认返回false
 *          如果不是同种类型对象,没有可比性,直接返回false.
 *      3.对称性：对于任何非空引用值 x 和 y，当且仅当y.equals(x) 返回 true 时，x.equals(y) 才应返回true.
 *      x == y
 *      说明
 *      y == x
 *      左边和右边比
 *      右边和左边比
 *      结果应该是一致的
 *
 *      4.传递性：对于任何非空引用值 x、y 和 z，如果x.equals(y)返回 true，并且 y.equals(z) 返回 true，那么x.equals(z) 应返回 true。
 *
 *      x == y
 *      y == z
 *      -->
 *      x == z
 *
 *      5一致性：对于任何非空引用值 x 和 y，多次调用 x.equals(y)始终返回 true 或始终返回 false。(但是前提是比较的条件没有被改变)
 *
 *      以上对称性,传递性,一致性如何满足呢?
 *      只需要使用对象成员变量的取值来判断对象相等,就自动满足这三点!!
 *
 * 注意事项:
 *      1.在实现排他性时，实际上有两种选择：
 *
 *          使用getClass方法比较。
 *          这个比较是比较苛刻的，只有在完全是同一个类型时才会返回true
 *          使用instanceof比较。
 *
 *          引用 instanceof 类名
 *          表示判断引用所指向的对象的具体类型，是否是后面类名的对象或者子类对象，如果是就返回true，如果不是就返回false。
 * 这个比较的条件就比较宽松了，可以允许传入子类对象。
 *      （当子类对象的父类成员和父类对象相同时,equals方法仍然返回true）
 *
 *      2.equals方法格式非常固定,可以用快捷键alt+insert自动生成.
 *
 *      3.对于Java的浮点数来说,double和float
 *          浮点数存在规格化,非规格化的区别
 *          而且还有一些特殊值,无穷大,无穷小,非数(NaN)等
 *          这意味浮点数比较大小,如果是一般情况
 *              用"=="或者"!="是完全没问题的
 *          但是碰到特殊情况就不能这么用的
 *
 *          所以我们比较浮点数的大小,不能用"=="或者"!="
 *          应该用包装类型中的compare方法
 *                  Float.compare(float a,float b)
 *                  Double.compare(double a,doublet b)
 *                  在记忆这个方法时,可以直接把这个方法看成a-b
 *                      这两个方法在,a < b时返回-1(负数)，
 *                      在a>b时，返回1(正数)，
 *                      只有在两个浮点数相等时，才会返回0
 *
 *       4.浮点数具有精度问题,在需要精密计算的场合,不建议使用浮点数
 *          如果需要计算小数,用BigDecimal类
 *
 *
 * @since 16:13
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        Student s1 = new Student(18, 100);
        Student s2 = new Student(18, 100);
        Student s3 = new Student(19, 100);


        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
        System.out.println(s1.equals(s3));

        /*Teacher t = new Teacher();
        Student s = new Student();
        System.out.println(s.equals(t));
        System.out.println(s.equals(s));*/
    }
}
class Teacher{}
class Student {
    int age;
    double score;

    public Student() {
    }

    public Student(int age, double score) {
        this.age = age;
        this.score = score;
    }

    // 重写equals方法
    @Override
    public boolean equals(Object obj) {
        // 1.首先要满足自反性
        if (this == obj) {
            return true;
        }
        // 2.实现排它性
        if (obj == null || this.getClass() != obj.getClass()) {
            return false;
        }
       // 3.用对象成员变量的取值来判断对象相等,从而满足对称性,一致性,传递性
        // 首先需要把obj引用强转成Student引用
        // 代码运行到这里,obj引用指向的对象一定是Student对象,所以无需判断直接做强转
        Student target = (Student) obj;
        // 判断属性是否相等
        if (this.age != target.age){
            return false;
        }
        // 运行到这里,age一定相等,只用判断score即可
        return Double.compare(score, target.score) == 0;
    }
}